﻿package com.frontalcode 
{
	import flash.utils.*;
	
	/**
	 * The ActionScriptInterpreter is actually an ECMAScript interpreter which 
	 * is largely a subset of AS3. A few language features are not supported 
	 * like "with" while other features from AS3 are supported like "as".
	 * 	
	 * The operation of the interpreter is as follows:
	 * 
	 * 1. Check the cache for the input code. 
	 * 2. If Result(1) is true then go to step 6.
	 * 3. Tokenize the input code.
	 * 4. Parse the token stream into a parse tree.
	 * 5. Store the parse tree in the cache.
	 * 6. Evaluate the parse tree.
	 * 7. Return the last result.
	 * 
	 * The technique for creating the parse tree is similar to that used by the
	 * Google v8 JavaScript parser. The technique for evaluating the parse tree
	 * is taken from the ECMAScript specification.
	 * 
	 * @see http://code.google.com/p/v8/
	 * @see http://www.ecma-international.org/publications/standards/Ecma-262.htm
	 */
    public class ActionScriptInterpreter extends OptionableObject
	{
		private var callStackDepth : Number = 0;
		
		/**
		 * @private
		 */
		static public var DEBUG_TOKENIZER : Number = 1;
		
		/**
		 * @private
		 */
		static public var DEBUG_PARSETREE : Number = 2;
		
		/**
		 * @private
		 */
		static public var DEBUG_EVALUATE  : Number = 4;
		
		/**
		 * @private
		 */
		static public var DEBUG_PROFILE   : Number = 8;
		
		/**
		 * @private
		 */
		static internal var ACTIONSCRIPT_TOKENS : Array = [
				{ precedence: 12, token: "+",			selfDelimiting: true, isUnaryOp: true },
				{ precedence:  2, token: "+=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: "[",			selfDelimiting: true },
				{ precedence:  0, token: "]",			selfDelimiting: true },
				{ precedence:  2, token: "=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  8, token: "&",			selfDelimiting: true },
				{ precedence:  2, token: "&=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence: 11, token: "<<",			selfDelimiting: true },
				{ precedence:  2, token: "<<=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: "~",			selfDelimiting: true, isUnaryOp: true },
				{ precedence:  6, token: "|",			selfDelimiting: true },
				{ precedence:  2, token: "|=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence: 11, token: ">>",			selfDelimiting: true },
				{ precedence:  2, token: ">>=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence: 11, token: ">>>",			selfDelimiting: true },
				{ precedence:  2, token: ">>>=",		selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  7, token: "^",			selfDelimiting: true },
				{ precedence:  2, token: "^=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: "/*",			selfDelimiting: true },
				{ precedence:  0, token: "*/",			selfDelimiting: true },
				{ precedence:  1, token: ",",			selfDelimiting: true },
				{ precedence:  3, token: "?",			selfDelimiting: true },
				{ precedence:  0, token: ":",			selfDelimiting: true },
				{ precedence:  0, token: "--",			selfDelimiting: true, isCountOperator: true },
				{ precedence: 13, token: "/",			selfDelimiting: true },
				{ precedence:  2, token: "/=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: ".",			selfDelimiting: true },
				{ precedence:  9, token: "==",			selfDelimiting: true, isCompareOperator: true },
				{ precedence: 10, token: ">",			selfDelimiting: true, isCompareOperator: true },
				{ precedence: 10, token: ">=",			selfDelimiting: true, isCompareOperator: true },
				{ precedence:  0, token: "++",			selfDelimiting: true, isCountOperator: true },
				{ precedence:  9, token: "!=",			selfDelimiting: true, isCompareOperator: true },
				{ precedence: 10, token: "is",			selfDelimiting: false, isCompareOperator: true },
				{ precedence: 10, token: "<",			selfDelimiting: true, isCompareOperator: true },
				{ precedence: 10, token: "<=",			selfDelimiting: true, isCompareOperator: true },
				{ precedence:  0, token: "//",			selfDelimiting: true },
				{ precedence:  5, token: "&&",			selfDelimiting: true },
				{ precedence:  0, token: "!",			selfDelimiting: true, isUnaryOp: true },
				{ precedence:  4, token: "||",			selfDelimiting: true },
				{ precedence: 13, token: "%",			selfDelimiting: true },
				{ precedence:  2, token: "%=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence: 13, token: "*",			selfDelimiting: true },
				{ precedence:  2, token: "*=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: "new",			selfDelimiting: false },
				{ precedence:  0, token: "{",			selfDelimiting: true },
				{ precedence:  0, token: "}",			selfDelimiting: true },
				{ precedence:  0, token: "(",			selfDelimiting: true },
				{ precedence:  0, token: ")",			selfDelimiting: true },
				{ precedence:  9, token: "===",			selfDelimiting: true, isCompareOperator: true },
				{ precedence:  9, token: "!==",			selfDelimiting: true, isCompareOperator: true },
				{ precedence:  0, token: "\"",			selfDelimiting: true },
				{ precedence:  0, token: "'",			selfDelimiting: true },
				{ precedence: 12, token: "-",			selfDelimiting: true, isUnaryOp: true },
				{ precedence:  2, token: "-=",			selfDelimiting: true, isAssignmentOperator: true },
				{ precedence:  0, token: ":",			selfDelimiting: true },
				{ precedence:  0, token: ";",			selfDelimiting: true },
				{ precedence:  0, token: "typeof",		selfDelimiting: false, isUnaryOp: true },
				{ precedence:  0, token: "void",		selfDelimiting: false, isUnaryOp: true },
				{ precedence:  0, token: "break",		selfDelimiting: false },
				{ precedence:  0, token: "continue",	selfDelimiting: false },
				{ precedence:  0, token: "delete",		selfDelimiting: false, isUnaryOp: true },
				{ precedence:  0, token: "do",			selfDelimiting: false },
				{ precedence:  0, token: "while",		selfDelimiting: false },
				{ precedence:  0, token: "else",		selfDelimiting: false },
				{ precedence:  0, token: "if",			selfDelimiting: false },
				{ precedence:  0, token: "for",			selfDelimiting: false },
				{ precedence:  0, token: "each",		selfDelimiting: false },
				{ precedence: 10, token: "in",			selfDelimiting: false },
				{ precedence:  0, token: "var",			selfDelimiting: false },
				{ precedence:  0, token: "this",		selfDelimiting: false },
				{ precedence:  0, token: "true",		selfDelimiting: false },
				{ precedence:  0, token: "false",		selfDelimiting: false },
				{ precedence:  0, token: "undefined",	selfDelimiting: false },
				{ precedence:  0, token: "null",		selfDelimiting: false },
				{ precedence:  0, token: "return",		selfDelimiting: false },
				{ precedence:  0, token: "with",		selfDelimiting: false },
				{ precedence:  0, token: "switch",		selfDelimiting: false },
				{ precedence:  0, token: "case",		selfDelimiting: false },
				{ precedence:  0, token: "default",		selfDelimiting: false },
				{ precedence:  0, token: "throw",		selfDelimiting: false },
				{ precedence:  0, token: "try",			selfDelimiting: false },
				{ precedence:  0, token: "catch",		selfDelimiting: false },
				{ precedence:  0, token: "finally",		selfDelimiting: false },
				{ precedence:  0, token: "function",	selfDelimiting: false },
				{ precedence: 13, token: "as",			selfDelimiting: false }
			];
			
		// For tokenizer.
		//
		
		private static var tokenizer : ActionScriptTokenizer = new ActionScriptTokenizer ( ACTIONSCRIPT_TOKENS );
		private var tokenStream : Object;
		
		// For parser.
		//
		
		private var currentCode : String;
		private var currentStatementCodeIndex : Number;
		private static var scriptsCache : Object = { };
		private var functionDeclarations : Array;
		private var variableDeclarations : Array;
		
		// For evaluator.
		//
		
		private var executionContexts : Array = [ ];
		private var documents : Array = [ ];
		static private var documentData : Dictionary = new Dictionary ( );
		private var defaultGlobalScope : Object = { }; // Only used if this interpreter is not associated with a document.
		
		/**
		 * @private
		 *
		 * Creates a new ActionScriptInterpreter instance.
		 */
		function ActionScriptInterpreter ( options : Object = null )
		{
			super ( options );
			
			setObjectOptionDefault ( "actionscriptInterpreterDebug", 0 );
			setObjectOptionDefault ( "actionscriptInterpreterUseFunctionClosures", true );
			setObjectOptionDefault ( "actionscriptInterpreterUseOOSugar", true );
			setOptionsFromLoaderParameters ( );
			if ( getObjectOption ( "actionscriptInterpreterUseOOSugar" ) ) useOOSugar ( );
		}
		
		static public function setPerDocumentOptions ( document : Document, options : Object = null )
		{
			if ( documentData [ document ] == null ) documentData [ document ] = {};
			documentData [ document ].options = options;
		}
		
		static public function getPerDocumentOptions ( document : Document )
		{
			if ( document == null || documentData [ document ] == null ) return null;
			return documentData [ document ].options;
		}
		
		static public function setDocumentGlobalScope ( document : Document, globals : Object )
		{
			if ( documentData [ document ] == null ) documentData [ document ] = {};
			documentData [ document ].globals = globals;
		}
		
		static public function getDocumentGlobalScope ( document : Document )
		{
			if ( document == null ) return null;
			if ( documentData [ document ] == null ) setDocumentGlobalScope ( document, {} );
			return documentData [ document ].globals;
		}
		
		static public function onDestroyDocument ( document : Document )
		{
			delete documentData [ document ];
		}
		
		static public function useOOSugar ( ) : void
		{
			Function.prototype.method = function (name, func) {
				this.prototype[name] = func;
				return this;
			};
			
			Function.prototype.inherits = function (parent) {
				var d = {}, p = (this.prototype = new parent());
				this.method('uber', function uber(name) {
					if (!(name in d)) {
						d[name] = 0;
					}        
					var f, r, t = d[name], v = parent.prototype;
					if (t) {
						while (t) {
							v = v.constructor.prototype;
							t -= 1;
						}
						f = v[name];
					} else {
						f = p[name];
						if (f == this[name]) {
							f = v[name];
						}
					}
					d[name] += 1;
					r = f.apply(this, arguments.slice(1));
					d[name] -= 1;
					return r;
				});
				return this;
			};
			
			Function.prototype.swiss = function (parent) {
				for (var i = 1; i < arguments.length; i += 1) {
					var name = arguments[i];
					this.prototype[name] = parent.prototype[name];
				}
				return this;
			};
			
			Function.prototype.bind = function(thisArg) {
				var bindArgs = arguments.slice(1);
				var method = this;
				return function ( ) {
						method.overrideImplicitThisValue = true;
						return method.apply ( thisArg, bindArgs.concat(arguments) );
					}
			};    
		}
		
		/**
		 * This will interpret a string of code.
		 * 	
		 * @param code				The string of code.
		 * @param thisObject		The evaluation of "this" when interpreting.
		 * @param localVariables	An object that will be used to create a
		 * 							series of local variables available during
		 * 							interpretation. For example, { x: 5 } will
		 * 							run the program with a variable "x" predefined
		 * 							to be 5.
		 * @param scope				The scope Object to use for the evaluation.		
		 * @param global			The global Object to use for the evaluation.
		 * @param document			A document to evaluate the script against. This
		 * 							enables the use of ExternalAssetManager's
		 * 							getDefinition.
		 * 
		 * Using variables without a "var" declaration will cause them to be 
		 * created/sought in the scope parameter (if specified). In this way, 
		 * variables may persist between calls to evaluate or operate on 
		 * variables that exists in the run-time environment. (Conversely, 
		 * "var"-declared variables will be destroyed when the evaluate call 
		 * completes.)
		 * 
		 * @return The last evaluation.
		 */
		static public function evaluate ( code : String, thisObject : Object = null, localVariables : Object = null, scope : Object = null, document : Document = null ) : Object
		{
			return new ActionScriptInterpreter ( getPerDocumentOptions ( document ) ).internalEvaluate ( code, thisObject, localVariables, scope, document );
		}
		
		/**
		 * @private
		 */
		internal function get globalScope ( ) : Object
		{
			var result : Object = null;
			if ( documents.length > 0 ) result = getDocumentGlobalScope ( documents [ documents.length - 1 ] );
			return result == null ? defaultGlobalScope : result;
		}
		
		/**
		 * @private
		 */
		internal function internalEvaluate ( code : String, thisObject : Object, localVariables : Object = null, scope : Object = null, document : Document = null ) : Object
		{
			if ( code == null || code.length == 0 ) return undefined;

			currentCode = code;
			currentStatementCodeIndex = 0;
			documents.push ( document ); 
			
			try
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE )
				{
					Profiler.getInstance ( ).enabled = true;
					Profiler.getInstance ( ).resetTimers ( );
					Profiler.getInstance ( ).startTimer ( "total" );
				}
		
				// Look for the code in our cache.
				//
				
				var program : Object = scriptsCache [ code ];
				
				if ( program == null )
				{
					// Parse the code into a token stream.
					//
					
					if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).startTimer ( "tokenize" );
					
					tokenStream = tokenizer.tokenizeCode ( code, ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_TOKENIZER ) == 1 );
						
					if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).stopTimer ( "tokenize" );
					
					if ( tokenStream.tokens.length == 0 ) return undefined;
					
					// Add the special end-of-input token.
					//
					
					tokenStream.tokens.push ( { token: "EndOfInput", precedence: 0 } );
					
					// Parse the token stream into a syntax tree.
					//
					
					if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).startTimer ( "parse" );
					
					callStackDepth = 0;
					program = parseProgram ( );
					
					if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).stopTimer ( "parse" );
					
					// Cache the program.
					//
					
					scriptsCache [ code ] = program;
				}
				
				// Run the program.
				//
				
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).startTimer ( "evaluate" );
				
				var result : Object = getValue ( evaluateSyntax ( program, thisObject, localVariables, scope ).value );
				
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE ) Profiler.getInstance ( ).stopTimer ( "evaluate" );
			}
			catch ( error : * )
			{
				var errorMsg : String = error + " Near \"" + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex ) + "\"";
			}
			finally
			{
				if ( errorMsg != null )
				{
					trace ( errorMsg );
					Debugger.logMessage ( Debugger.ERROR, "ActionScriptInterpreter", errorMsg );
				}
				
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PROFILE )
				{
					Profiler.getInstance ( ).stopTimer ( "total" );
					Profiler.getInstance ( ).logTimers ( );
					Profiler.getInstance ( ).enabled = false;
				}
				
				documents.pop ( );  
			}
			
			return result;
		}
		
		/*
			Function: peek
			
				Parser helper function - return the current token being processed.
				
			Returns:
			
				A token definition.
		*/
		/**
		 * @private
		 */
		internal function peek ( ) : Object
		{
			return tokenStream.tokens [ tokenStream.index ];
		}
		
		/*
			Function: next
			
				Parser helper function - consume the current token.
				
			Returns:
			
				The consumed token.
		*/
		/**
		 * @private
		 */
		internal function next ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( "consuming token " + Debugger.objectToString ( tokenStream.tokens [ tokenStream.index ], 0, 2, false ) );
			
			return tokenStream.tokens [ tokenStream.index++ ];
		}
		
		/*
			Function: expect
			
				Consume the expected tokens. 
				
			Parameters:
			
				tokens	- An array of expected tokens.
				
			Exceptions:
			
				Throws an error if the tokens in the token stream were not expected.
		*/
		/**
		 * @private
		 */
		internal function expect ( tokens : Array ) : void
		{
			for ( var i : int = 0; i < tokens.length; i++ )
			{
				var nextToken : Object = next ( );
				
				if ( nextToken.token != tokens [ i ] )
				{
					throw new FrontalError ( "\"" + nextToken.token + "\". Expected \"" + tokens [ i ] + "\" at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, nextToken.codeIndex ), FrontalError.UNEXPECTED_TOKEN );
				}
			}
		}
		
		/*
			Function: expectSemicolon
			
				Expect a semicolon real or implied.
				
			Exceptions:
			
				Throws an error if the tokens in the token stream were not expected.
		*/
		/**
		 * @private
		 */
		internal function expectSemicolon ( ) : void
		{
			var tokenDefn : Object = peek ( );
			
			if ( tokenDefn.token == ";" )
			{
				next ( );
				return;
			}
			
			if ( tokenDefn.hasPrecedingLineTerminator || tokenDefn.token == "}" || tokenDefn.token == "EndOfInput" )
			{
				return;
			}
			
			expect ( [ ";" ] );
		}
		
		/*
			Function: evaluateSyntax
			
				General function to evaluate a node in the syntax tree.
				
			Parameters:
			
				syntax	- A node in the syntax tree.
				
			Returns:
			
				The evaluation of the syntax node.
		*/
		/**
		 * @private
		 */
		internal function evaluateSyntax ( syntax : Object, ... args ) : Object
		{
			args.unshift ( syntax );
			
			if ( ! isNaN ( syntax.codeIndex ) ) currentStatementCodeIndex = syntax.codeIndex;
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 1 ) + "evaluate " + Debugger.objectToString ( syntax, 0, 2, false ) );
			
			if ( syntax.objectType == "Value" || syntax.objectType == "Reference" || syntax.objectType == "Completion" )
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( -1 ) + "return " + Debugger.objectToString ( syntax, 0, 2, false ) );
				
				return syntax;
			}
			else
			{
				var result : Object = this [ "evaluate" + syntax.objectType ].apply ( this, args );
				
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( -1 ) + "return " + Debugger.objectToString ( result, 0, 2, false ) );
				
				return result;
			}
		}
		
		/*
			The following functions (parse* and evaluate*) are used to either parse
			the token stream into a syntax tree or evaluate the nodes in teh syntax 
			tree.
		*/
		
		/**
		 * @private
		 */
		internal function parseProgram ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseProgram" );
			
			var result : Object = { objectType: "Program", body: [ ] };
			
			parseSourceElements ( result, "EndOfInput" );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseProgram returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateProgram ( syntax : Object, theThis : Object, localVariables : Object, scope : Object ) : Object
		{
			try
			{
				var executionContext : Object = { theThis: theThis == null ? globalScope : theThis, scopeChain: [ globalScope ] };
				if ( scope != null ) executionContext.scopeChain.unshift ( scope );
				executionContexts.push ( executionContext );
				
				// Setup our local variables in the interpreter.
				
				for ( var varName : String in localVariables )
				{
					evaluateSyntax ( { objectType: "VariableDeclaration", name: { objectType: "Identifier", name: varName }, value: { objectType: "Value", value: localVariables [ varName ] } } );
				}
				
				var result : Object = { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
				
				for ( var i : int = 0; i < syntax.declarations.length; i++ )
				{
					// Handle any function and variable declarations before we
					// begin execution.
					//
					
					evaluateSyntax ( syntax.declarations [ i ] );
				}
				
				for ( i = 0; i < syntax.body.length; i++ )
				{
					result = evaluateSyntax ( syntax.body [ i ] );
		
					if ( result.objectType != "Completion" )
					{
						result = { objectType: "Completion", type: "normal", value: getValue ( result ), target: "empty" };
					}
					
					if ( result.type == "throw" ) throw result.value;
					if ( result.type != "normal" ) break;
				}
			}
			finally
			{
				executionContexts.pop ( );
			}

			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseSourceElements ( result : Object, endToken : String ) : void
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseSourceElements" );
			
			// Variable and function declarations will be evaluated 
			// before the rest of the program so that these symbols are 
			// available and reserved in the current scope. We keep them
			// separate as function declaration should always precede
			// variable declarations. (See ECMA262 10.1.3)
			//
			// Any declarations are for this particular execution context and 
			// not that of any surrounding code.
			//
			var saveFunctionDeclarations : Array = functionDeclarations;
			var saveVariableDeclarations : Array = variableDeclarations;
			functionDeclarations = [ ];
			variableDeclarations = [ ];
			
			result.body = [ ];
			while ( peek ( ).token != endToken )
			{
				result.body.push ( parseStatement ( ) );
			}
			
			result.declarations = functionDeclarations.concat ( variableDeclarations );
			functionDeclarations = saveFunctionDeclarations;
			variableDeclarations = saveVariableDeclarations;
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseSourceElements returns" );
		}
		
		/**
		 * @private
		 */
		internal function parseStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseStatement" );
			
			var result : Object;
			
			var tokenDefn : Object = peek ( );
			
			currentStatementCodeIndex = tokenDefn.codeIndex;
			
			switch ( tokenDefn.token )
			{
				case "{":
					result = parseBlock ( );
					break;
					
				case "var":
					result = parseVariableStatement ( );
					break;
					
				case ";":
					next ( );
					result = { objectType: "EmptyStatement" };
					break;
					
				case "if":
					result = parseIfStatement ( );
					break;
					
				case "do":
					result = parseDoStatement ( );
					break;
					
				case "while":
					result = parseWhileStatement ( );
					break;
					
				case "for":
					result = parseForStatement ( );
					break;
					
				case "continue":
					result = parseContinueStatement ( );
					break;
					
				case "break":
					result = parseBreakStatement ( );
					break;
					
				case "return":
					result = parseReturnStatement ( );
					break;
					
				case "switch":
					result = parseSwitchStatement ( );
					break;
					
				case "function":
					result = parseFunctionDeclaration ( );
					break;
				
				case "throw":
					result = parseThrowStatement ( );
					break;
					
				case "try":
					result = parseTryStatement ( );
					break;
				
				case "with":
					throw new FrontalError ( tokenDefn.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, tokenDefn.codeIndex ), FrontalError.UNIMPLEMENTED_LANGUAGE_ELEMENT );
					break;
					
				default:
					result = parseExpressionStatement ( );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			result.codeIndex = currentStatementCodeIndex;
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateEmptyStatement ( syntax : Object ) : Object
		{
			return { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseBlock ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseBlock" );
			
			var result : Object = { objectType: "Block", statements: [ ] };
			
			expect ( [ "{" ] );
			
			while ( peek ( ).token != "}" )
			{
				result.statements.push ( parseStatement ( ) );
			}
			
			expect ( [ "}" ] );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseBlock returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateBlock ( syntax : Object ) : Object
		{ 
			var result : Object = { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
			
			for ( var i : int = 0; i < syntax.statements.length; i++ )
			{
				result = evaluateSyntax ( syntax.statements [ i ] );
	
				if ( result.objectType != "Completion" )
				{
					result = { objectType: "Completion", type: "normal", value: getValue ( result ), target: "empty" };
				}
				
				if ( result.type != "normal" ) break;
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseVariableStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseVariableStatement" );
			
			var result : Object = parseVariableDeclarations ( );
			
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseVariableStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
	
		/**
		 * @private
		 */
		internal function parseVariableDeclarations ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseVariableDeclarations" );
			
			var result : Object = { objectType: "VariableDeclarations", statements: [ ] };
			
			expect ( [ "var" ] );
			
			var varCount : Number = 0;
			
			do
			{
				if ( varCount > 0 )
				{
					next ( );
				}
				
				var name : Object = parseIdentifier ( );
				varCount++;
				
				// Gather any type information even though we don't use it for
				// now.
				//
				var type : Object;
				if ( peek ( ).token == ":" )
				{
					next ( );
					type = parseIdentifier ( );
				}
				
				var value : Object;
				if ( peek ( ).token == "=" )
				{
					next ( );
					value = parseAssignmentExpression ( );
				}
				
				// Add the declaration to our list of variable declarations. The
				// variable will be created in the current scope before the 
				// script is run though its value (if there is any) will not be
				// applied until we hit this particular line. (See ECMA262 
				// 10.1.3)
				//
				variableDeclarations.push ( { objectType: "VariableDeclaration", name: name, value: null, type: type } ); 

				result.statements.push ( { objectType: "VariableDeclaration", name: name, value: value } ); 
			}
			while ( peek ( ).token == "," );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseVariableDeclarations returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateVariableDeclaration ( syntax : Object ) : Object
		{
			var value : Object = undefined;
			
			if ( syntax.value != undefined )
			{
				value = getValue ( evaluateSyntax ( syntax.value ) );
			}
			
			// Mark this syntax object as being part of a variable declaration 
			// so that we do not even search the "this" object during 
			// evaluation.
			//
			syntax.name.skipThisScope = true;
			var nameResult : Object = evaluateSyntax ( syntax.name );
			
			// We store the value in the top scope of our current 
			// program's execution context.
			//
			
			var currentExecutionContext : Object = executionContexts [ executionContexts.length - 1 ];
			nameResult.baseObject = currentExecutionContext.scopeChain [ 0 ];
			putValue ( nameResult, value );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 0 ) + "set var " + syntax.name.name + " = " + Debugger.objectToString ( value, 0, 2, false ) );
			
			return undefined;
		}
		
		/**
		 * @private
		 */
		internal function evaluateVariableDeclarations ( syntax : Object ) : Object
		{ 
			var result : Object = { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
			
			for ( var i : int = 0; i < syntax.statements.length; i++ )
			{
				evaluateSyntax ( syntax.statements [ i ] );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseExpressionStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseExpressionStatement" );
			
			var result : Object = { objectType: "ExpressionStatement", expr: parseExpression ( ) };
			
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseExpressionStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateExpressionStatement ( syntax : Object ) : Object
		{
			return { objectType: "Completion", type: "normal", value: getValue ( evaluateSyntax ( syntax.expr ) ), target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseIfStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseIfStatement" );
			
			var result : Object = { objectType: "IfStatement" };
			
			expect ( [ "if", "(" ] );
			result.cond = parseExpression ( );
			expect ( [ ")" ] );
			result.thenStatement = parseStatement ( );
			if ( peek ( ).token == "else" )
			{
				next ( );
				result.elseStatement = parseStatement ( );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseIfStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateIfStatement ( syntax : Object ) : Object
		{
			var cond : Object = evaluateSyntax ( syntax.cond );
			
			var result : Object;
			
			if ( getValue ( cond ) )
			{
				result = evaluateSyntax ( syntax.thenStatement );
			}
			else
			{
				if ( syntax.elseStatement != undefined )
				{
					result = evaluateSyntax ( syntax.elseStatement );
				}
				else
				{
					result = { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
				}
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseContinueStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseContinueStatement" );
			
			var result : Object = { objectType: "ContinueStatement" };
			
			expect ( [ "continue" ] );
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseContinueStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateContinueStatement ( syntax : Object ) : Object
		{
			return { objectType: "Completion", type: "continue", value: "empty", target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseBreakStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseBreakStatement" );
			
			var result : Object = { objectType: "BreakStatement" };
			
			expect ( [ "break" ] );
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseBreakStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateBreakStatement ( syntax : Object ) : Object
		{
			return { objectType: "Completion", type: "break", value: "empty", target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseReturnStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseReturnStatement" );
			
			var result : Object = { objectType: "ReturnStatement" };
			
			expect ( [ "return" ] );
			
			var tokenDefn : Object = peek ( );
			
			if ( tokenDefn.hasPrecedingLineTerminator || tokenDefn.token == ";" || tokenDefn.token == "}" || tokenDefn.token == "EndOfInput" )
			{
				expectSemicolon ( );
				result.expression = { objectType: "Value", value: undefined };
				return result;
			}
			
			result.expression = parseExpression ( );
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseReturnStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateReturnStatement ( syntax : Object ) : Object
		{
			var result : Object = { objectType: "Completion", type: "return", value: undefined, target: "empty" };
			
			if ( syntax.expression != undefined )
			{
				result.value = getValue ( evaluateSyntax ( syntax.expression ) );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseCaseClause ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseCaseClause" );
			
			var result : Object = { objectType: "CaseClause", op: peek ( ), statements: [ ] };
			
			if ( peek ( ).token == "case" )
			{
				expect ( [ "case" ] );
				result.expression = parseExpression ( );
			}
			else
			{
				expect ( [ "default" ] );
			}
			
			expect ( [ ":" ] );
			
			var token : String;
			
			while ( ( token = peek ( ).token ) != "case" && token != "default" && token != "}" )
			{
				result.statements.push ( parseStatement ( ) );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseCaseClause returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateCaseClause ( syntax : Object, switchValue : Object ) : Object
		{
			return getValue ( evaluateSyntax ( syntax.expression ) );
		}
		
		// XXX Does not throw an error for multiple default statements.
		//
		
		/**
		 * @private
		 */
		internal function parseSwitchStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseSwitchStatement" );
			
			var result : Object = { objectType: "SwitchStatement", cases: [ ] };
			
			expect ( [ "switch", "(" ] );
			result.tag = parseExpression ( );
			expect ( [ ")", "{" ] );
			
			while ( peek ( ).token != "}" )
			{
				result.cases.push ( parseCaseClause ( ) );
			}
			expect ( [ "}" ] );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseSwitchStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateSwitchStatement ( syntax : Object ) : Object
		{
			var result : Object = { objectType: "Completion", type: "normal", value: "empty", target: "empty" };
			
			var switchValue : Object = getValue ( evaluateSyntax ( syntax.tag ) );
		
			// First, see if we match any cases.
			//
			var matched : int = -1;
			for ( var i : int = 0; i < syntax.cases.length; i++ )
			{
				if ( syntax.cases [ i ].op.token == "case" )
				{
					var caseClauseResult : Object = evaluateSyntax ( syntax.cases [ i ], switchValue );
					
					if ( caseClauseResult === switchValue )
					{
						matched = i;
						break;
					}
				}
			}
			
			// Now evaluate all applicable statements.
			//
			var runStatements : Boolean = false;
			for ( i = 0; i < syntax.cases.length; i++ )
			{
				if ( i == matched || ( matched == -1 && syntax.cases [ i ].op.token == "default" ) ) runStatements = true;

				if ( runStatements )
				{				
					for ( var j : int = 0; j < syntax.cases [ i ].statements.length; j++ )
					{
						result = evaluateSyntax ( syntax.cases [ i ].statements [ j ] );
						if ( result.type != "normal" ) 
						{
							if ( result.type == "break" ) result.type = "normal";
							return result;
						}
					}
				}
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseFunctionDeclaration ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseFunctionDeclaration" );
			
			expect ( [ "function" ] );
			
			var result : Object;
			var name : Object;
			
			if ( peek ( ).token == "Identifier" )
			{
				name = parseIdentifier ( );
			}
			
			var func : Object = parseFunctionLiteral ( name );
			
			if ( name == null )
			{
				result = { objectType: "ExpressionStatement", expr: func };
			}
			else
			{
				result = { objectType: "EmptyStatement" };
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseFunctionDeclaration returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseFunctionLiteral ( name : Object ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseFunctionLiteral" );
			
			var result : Object = { objectType: "FunctionLiteral", body: [ ], parameters: [ ] };
			
			expect ( [ "(" ] );
			
			var done : Boolean = peek ( ).token == ")";
			
			while ( ! done )
			{
				result.parameters.push ( parseIdentifier ( ) );
			
				// Gather any type information for the parameter even though we 
				// don't use it for now.
				//
				var argType : Object;
				if ( peek ( ).token == ":" )
				{
					next ( );
					argType = parseIdentifier ( );
				}
				
				done = peek ( ).token == ")";
			
				if ( ! done ) expect ( [ "," ] );
			}
			
			expect ( [ ")" ] );
			
			// Gather any type information for the return even though we don't 
			// use it for now.
			//
			var returnType : Object;
			if ( peek ( ).token == ":" )
			{
				next ( );
				returnType = parseIdentifier ( );
			}
				
			expect ( [ "{" ] );
			
			parseSourceElements ( result, "}" );
			
			expect ( [ "}" ] );
			
			if ( name != null )
			{
				functionDeclarations.push ( { objectType: "VariableDeclaration", name: name, value: result, returnType: returnType } );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseFunctionLiteral returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateFunctionLiteral ( syntax : Object ) : Object
		{ 
			var func : Function = function ( ) : *
				{
					try
					{
						var interp : ActionScriptInterpreter = new ActionScriptInterpreter ( );
						
						if ( interp.getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) && arguments.callee.isFrontalConstructor )
						{
							// Here an interpreted function is being used as a
							// constructor. We fixup the closures on all of the
							// methods so that they will behave properly in
							// contexts like an event callback. See the method 
							// constructInstance below for more details.
							//
							for ( var x : String in arguments.callee.prototype )
							{
								if ( this [ x ] is Function )
								{
									if ( ! this [ x ].isWrapper )
									{
										var thisWrapper : Function = function ( ) : *
											{
												var method : Function = arguments.callee.method;
												var theThis : * = arguments.callee.theThis;
												return method.apply ( theThis, arguments );
											}
										thisWrapper [ "method" ] = this [ x ];
										thisWrapper [ "theThis" ] = this;
										thisWrapper [ "isWrapper" ] = true;
										this [ x ] = thisWrapper;
									}
									else
									{
										this [ x ] [ "theThis" ] = this;
									}
								}
							}
						}
						
						var syntax : Object = arguments.callee.syntax;
						interp.executionContexts = arguments.callee.executionContexts;
						
						var theThis : * = this; 
						if ( interp.getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) ) {
							// overrideImplicitThisValue is to satisfy 6.a.i from
							// ECMA 262 11.2.3 Function Calls:
							//
							// 6. If Type(ref) is Reference, then
							//   a. If IsPropertyReference(ref) is true, then
							//     i. Let thisValue be GetBase(ref).
							//   b. Else, the base of ref is an Environment Record
							//     i. Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
							//
							theThis = arguments.callee.theThis != null && ! arguments.callee.overrideImplicitThisValue ? arguments.callee.theThis : this;
							arguments.callee.overrideImplicitThisValue = false;
						}
						
						// XXX This is copied code from internalEvaluate.
						// We need a way to keep it in one place.
						//
						interp.documents.push ( arguments.callee.document );

						// We create a new execution scope for each function 
						// call. (See ECMA262 10.2) For function code, we 
						// initialize the scope chain to the activation object
						// followed by the scope when the function was created. 
						// (See ECMA262 10.2.3)
						//
						var newScopeChain : Array = arguments.callee.scopeChain.slice ( );
						newScopeChain.unshift ( { } );
						
						// "this" is provided by our caller. If it is null then
						// it is the global scope. (See ECMA262 10.2.3)
						//
						interp.executionContexts.push ( { theThis: theThis == null ? globalScope : theThis, scopeChain: newScopeChain } );
						
						var result : Object;
						
						for ( var i : int = 0; i < syntax.declarations.length; i++ )
						{
							// Handle any function and variable declarations before we
							// begin execution.
							//
							evaluateSyntax ( syntax.declarations [ i ] );
						}
						
						// Setup the arguments in the current scope.
						//
						interp.evaluateSyntax ( { objectType: "VariableDeclaration", name: { objectType: "Identifier", name: "arguments" }, value: { objectType: "Value", value: arguments } } );
						
						// Setup the parameters in the current scope.
						//
						for ( i = 0; i < syntax.parameters.length; i++ )
						{
							interp.evaluateSyntax ( { objectType: "VariableDeclaration", name: syntax.parameters [ i ], value: { objectType: "Value", value: arguments [ i ] } } );
						}
						
						for ( i = 0; i < syntax.body.length; i++ )
						{
							result = interp.evaluateSyntax ( syntax.body [ i ] );
				
							if ( result.objectType != "Completion" )
							{
								result = { objectType: "Completion", type: "normal", value: getValue ( result ), target: "empty" };
							}
							
							if ( result.type != "normal" ) break;
						}
					}
					catch ( error : * )
					{
						var errorMsg : String = error + " Near \"" + tokenizer.getCodeSnippet ( arguments.callee.currentCode, interp.currentStatementCodeIndex ) + "\"";
					}
					finally
					{
						if ( interp.getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) ) arguments.callee.overrideImplicitThisValue = false;
						
						if ( errorMsg != null )
						{
							trace ( errorMsg );
							Debugger.logMessage ( Debugger.ERROR, "ActionScriptInterpreter", errorMsg );
						}
						
						interp.documents.pop ( );
						interp.executionContexts.pop ( );
					}
					
					if ( result != null && result.type == "return" ) return interp.getValue ( result.value );
				};
	
			// When creating a function object, we need to store the current 
			// scope so it can be copied into the function's execution context
			// when it is called. (See ECMA262 13.2)
			//
			func [ "isFrontalFunction" ] = true;
			func [ "syntax" ] = syntax;
			func [ "executionContexts" ] = executionContexts;
			func [ "scopeChain" ] = executionContexts [ executionContexts.length - 1 ].scopeChain;
			func [ "globalScope" ] = globalScope;
			func [ "document" ] = documents.length > 0 ? documents [ documents.length - 1 ] : null;
			func [ "currentCode" ] = currentCode;
			func [ "currentStatementCodeIndex" ] = currentStatementCodeIndex;
			if ( getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) ) {
				func [ "theThis" ] = getValue ( evaluateThisValue ( ) );
				func [ "overrideImplicitThisValue" ] = false;
			}
	
			return { objectType: "Value", value: func };
		}
		
		/**
		 * @private
		 */
		internal function parseThrowStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseThrowStatement" );

			var result : Object = { objectType: "ThrowStatement", expression: null };
			
			expect ( [ "throw" ] );
			
			if ( peek ( ).hasPrecedingLineTerminator )
			{
				throw new FrontalError ( "\"throw\" must not be followed by a newline at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex ), FrontalError.SYNTAX_ERROR );
			}
			
			result.expression = parseExpression ( );
			
			expectSemicolon ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseThrowStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateThrowStatement ( syntax : Object ) : void
		{ 
			throw getValue ( evaluateSyntax ( syntax.expression ) );
		}
		
		/**
		 * @private
		 */
		internal function parseTryStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseTryStatement" );
			
			var result : Object = { objectType: "TryStatement", tryBlock: null, catchName: null, catchBlock: null, finallyBlock: null };
			
			expect ( [ "try" ] );
			
			result.tryBlock = parseBlock ( );

			var token : Object = peek ( );
			
			if ( token.token != "catch" && token.token != "finally" )
			{
				throw new FrontalError ( "\"try\" requires a \"catch\" or \"finally\" at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex ), FrontalError.SYNTAX_ERROR );
			}
			
			var hasCatch : Boolean = false;
			
			if ( token.token == "catch" )
			{
				hasCatch = true;
				next ( );
			
				// XXX This try statement does not support catches based on 
				// type.
				//
				expect ( [ "(" ] );
				result.catchName = parseIdentifier ( );
				expect ( [ ")" ] );
				
				if ( peek ( ).token == "{" )
				{
					result.catchBlock = parseBlock ( );
				}
				else
				{
					expect ( [ "{" ] );
				}
				
				token = peek ( );
			}
			
			if ( token.token == "finally" || ! hasCatch )
			{
				expect ( [ "finally" ] );
				result.finallyBlock = parseBlock ( );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseTryStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateTryStatement ( syntax : Object ) : Object
		{ 
			var result : Object; 
		
			var gotException : Boolean = false;
			
			try 
			{ 
				result = evaluateSyntax ( syntax.tryBlock ); 
			} 
			catch ( e : * )
			{
				result = { objectType: "Completion", type: "throw", value: e, target: "empty" };
				
				gotException = true;
				
				if ( syntax.catchBlock != null )
				{
					try
					{
						// According to section 12.14 of the ECMA standard, we add
						// the exception to the front of the scope chain. We remove
						// it after we evaluate the block.
						//
						executionContexts [ executionContexts.length - 1 ].scopeChain.unshift ( { } );
						evaluateSyntax ( { objectType: "VariableDeclaration", name: syntax.catchName, value: { objectType: "Value", value: e } } );
						result = evaluateSyntax ( syntax.catchBlock );
					}
					finally
					{
						executionContexts [ executionContexts.length - 1 ].scopeChain.shift ( );
					}
				}
			}
			finally
			{
				if ( syntax.finallyBlock != null ) 
				{
					var finallyResult : Object = evaluateSyntax ( syntax.finallyBlock );
					if ( finallyResult.type != "normal") result = finallyResult;
				}
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseDoStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseDoStatement" );
			
			var result : Object = { objectType: "DoStatement" };
			
			expect ( [ "do" ] );
			result.body = parseStatement ( );
			expect ( [ "while", "(" ] );
			result.cond = parseExpression ( );
			expect ( [ ")" ] );
			if ( peek ( ).token == ";" )
			{
				next ( );
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseDoStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateDoStatement ( syntax : Object ) : Object
		{ 
			var V : Object = "empty"; 
		
			var lastCondResult : Object;
			
			do 
			{ 
				var result : Object = evaluateSyntax ( syntax.body ); 
		
				if ( result.value != "empty" ) 
				{ 
					V = result.value; 
				} 
				
				if ( result.type == "continue" /* XXX && result.target in current label set */ ) 
				{
					continue;
				}
				else if ( result.type == "break" /* XXX && result.target in current label set */ ) 
				{ 
					return { objectType: "Completion", type: "normal", value: V, target: "empty" }; 
				} 
				else if ( result.type != "normal" ) 
				{ 
					return result; 
				} 
				
				lastCondResult = evaluateSyntax ( syntax.cond ); 
			} 
			while ( getValue ( lastCondResult ) );
			
			return { objectType: "Completion", type: "normal", value: V, target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseWhileStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseWhileStatement" );
			
			var result : Object = { objectType: "WhileStatement" };
			
			expect ( [ "while", "(" ] );
			result.cond = parseExpression ( );
			expect ( [ ")" ] );
			result.body = parseStatement ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseWhileStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateWhileStatement ( syntax : Object ) : Object
		{ 
			var V : Object = "empty"; 
		
			var lastCondResult : Object = evaluateSyntax ( syntax.cond );
			
			while ( getValue ( lastCondResult ) )
			{ 
				var result : Object = evaluateSyntax ( syntax.body ); 
		
				if ( result.value != "empty" ) 
				{ 
					V = result.value; 
				} 
				
				if ( result.type == "continue" /* XXX && result.target in current label set */ ) 
				{
					continue;
				}
				else if ( result.type == "break" /* XXX && result.target in current label set */ ) 
				{ 
					return { objectType: "Completion", type: "normal", value: V, target: "empty" }; 
				} 
				else if ( result.type != "normal" ) 
				{ 
					return result; 
				} 
				
				lastCondResult = evaluateSyntax ( syntax.cond ); 
			} 
			
			return { objectType: "Completion", type: "normal", value: V, target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseForStatement ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseForStatement" );
			
			var result : Object = { objectType: "ForStatement", each: false };
			
			expect ( [ "for" ] );
			if ( peek ( ).token == "each" ) 
			{
				next ( );
				result.each = true;
			}
			expect ( [ "(" ] );
			if ( peek ( ).token != ";" )
			{
				if ( peek ( ).token == "var" )
				{
					result.init = parseVariableDeclarations ( );
					if ( peek ( ).token == "in" )
					{
						expect ( [ "in" ] );
						result.enumerable = parseExpression ( );
						expect ( [ ")" ] );
						result.body = parseStatement ( );
						if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseForStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
						return result;
					}
				}
				else
				{
					// We can't accept the "in" operator during this parse call so
					// we temporarily set its precedence to a low value that will
					// cause it to stop the parse.
					//
					
					var savePrecedence : Number = tokenizer.allTokensHash [ "in" ].precedence;
					tokenizer.allTokensHash [ "in" ].precedence = 0;
					result.init = parseExpression ( );
					tokenizer.allTokensHash [ "in" ].precedence = savePrecedence;
					
					if ( peek ( ).token == "in" )
					{
						expect ( [ "in" ] );
						result.enumerable = parseExpression ( );
						expect ( [ ")" ] );
						result.body = parseStatement ( );
						if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseForStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
						return result;
					}
					else
					{
						if ( result.each ) throw new FrontalError ( "\"for each\" requires \"in\" operator at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex ), FrontalError.SYNTAX_ERROR );
						result.init = { objectType: "ExpressionStatement", expr: result.init };
					}
				}
			}
			
			expect ( [ ";" ] );
			if ( peek ( ).token != ";" )
			{
				result.cond = parseExpression ( );
			}
			expect ( [ ";" ] );
			if ( peek ( ).token != ")" )
			{
				result.next = { objectType: "ExpressionStatement", expr: parseExpression ( ) };
			}
			expect ( [ ")" ] );
			result.body = parseStatement ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseForStatement returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateForStatement ( syntax : Object ) : Object
		{ 
			var V : Object = "empty"; 
		
			var initVar : Object;
			
			if ( syntax.init != undefined )
			{
				initVar = syntax.init;
				
				if ( syntax.init.objectType == "VariableDeclarations" )
				{
					evaluateSyntax ( syntax.init );
					initVar = syntax.init.statements [ 0 ].name;
				}
			}
			
			var enumerableIndex : Number = 0;
			var enumerableProperties : Array;
			
			if ( syntax.enumerable != undefined )
			{
				enumerableProperties = [ ];
				
				if ( syntax.each )
				{
					for each ( var i : * in getValue ( evaluateSyntax ( syntax.enumerable ) ) )
					{
						enumerableProperties.push ( i );
					}
				}
				else
				{
					for ( i in getValue ( evaluateSyntax ( syntax.enumerable ) ) )
					{
						enumerableProperties.push ( i );
					}
				}
			}
			
			var lastCondResult : Object;
			
			if ( syntax.cond != undefined )
			{
				lastCondResult = evaluateSyntax ( syntax.cond );
			}
			
			while ( 
				( syntax.enumerable != undefined && enumerableIndex < enumerableProperties.length ) ||
				( syntax.enumerable == undefined && syntax.cond == undefined || getValue ( lastCondResult ) ) )
			{ 
				if ( syntax.enumerable != undefined && initVar != null )
				{
					putValue ( evaluateSyntax ( initVar ), enumerableProperties [ enumerableIndex++ ] );
				}
				
				var result : Object = evaluateSyntax ( syntax.body ); 
		
				if ( result.value != "empty" ) 
				{ 
					V = result.value; 
				} 
				
				if ( result.type == "continue" /* XXX && result.target in current label set */ ) 
				{
					// Fall through.
				}
				else if ( result.type == "break" /* XXX && result.target in current label set */ ) 
				{ 
					return { objectType: "Completion", type: "normal", value: V, target: "empty" }; 
				} 
				else if ( result.type != "normal" ) 
				{ 
					return result; 
				} 
				
				if ( syntax.next != undefined )
				{
					evaluateSyntax ( syntax.next );
				}
				
				if ( syntax.cond != undefined )
				{
					lastCondResult = evaluateSyntax ( syntax.cond );
				}
			} 
			
			return { objectType: "Completion", type: "normal", value: V, target: "empty" };
		}
		
		/**
		 * @private
		 */
		internal function parseExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseExpression" );
			
			var result : Object = parseAssignmentExpression ( );
			
			while ( peek ( ).token == "," )
			{
				expect ( [ "," ] );
				var newResult : Object = { objectType: "BinaryOperation", op: tokenizer.allTokensHash [ "," ] };
				newResult.x = parseAssignmentExpression ( );
				newResult.y = result;
				result = newResult;
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateBinaryOperation ( syntax : Object ) : Object
		{
			if ( syntax.op.token != "&&" && syntax.op.token != "||" )
			{
				var xValue : * = getValue ( evaluateSyntax ( syntax.x ) );
				var yValue : * = getValue ( evaluateSyntax ( syntax.y ) );
			}
			
			var result : Object = { objectType: "Value" };
			
			switch ( syntax.op.token )
			{
				case "+":
					result.value = xValue + yValue;
					break;
				case "&":
					result.value = xValue & yValue;
					break;
				case "|":
					result.value = xValue | yValue;
					break;
				case ">>":
					result.value = xValue >> yValue;
					break;
				case ">>>":
					result.value = xValue >>> yValue;
					break;
				case "<<":
					result.value = xValue << yValue;
					break;
				case "^":
					result.value = xValue ^ yValue;
					break;
				case ",":
					result.value = yValue;
					break;
				case "/":
					result.value = xValue / yValue;
					break;
				case "||":
					// Be careful about evaluating || because we only want to
					// evaluate each syntax as necessary.
					//
					result.value = getValue ( evaluateSyntax ( syntax.x ) ) || getValue ( evaluateSyntax ( syntax.y ) );
					break;
				case "&&":
					// Be careful about evaluating || because we only want to
					// evaluate each syntax as necessary.
					//
					result.value = getValue ( evaluateSyntax ( syntax.x ) ) && getValue ( evaluateSyntax ( syntax.y ) );
					break;
				case "%":
					result.value = xValue % yValue;
					break;
				case "*":
					result.value = xValue * yValue;
					break;
				case "as":
					result.value = xValue as yValue;
					break;
				case "-":
					result.value = xValue - yValue;
					break;
				default:
					throw new FrontalError ( syntax.op.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.UNKNOWN_OPERATOR );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseAssignmentExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseAssignmentExpression" );
			
			var result : Object = parseConditionalExpression ( );
			
			if ( ! peek ( ).isAssignmentOperator )
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseAssignmentExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
				return result;
			}
			
			var newResult : Object = { objectType: "AssignmentExpression" };
			newResult.op = next ( );
			newResult.right = parseAssignmentExpression ( );
			newResult.left = result;
			result = newResult;
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseAssignmentExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateAssignmentExpression ( syntax : Object ) : Object
		{
			var left : Object = evaluateSyntax ( syntax.left );
			var leftValue : * = syntax.op.token != "=" ? getValue ( left ) : null;
			var right : Object = evaluateSyntax ( syntax.right );
			var rightValue : * = getValue ( right );
			
			var result : Object = { objectType: "Value" };
			
			switch ( syntax.op.token )
			{
				case "+=":
					leftValue += rightValue;
					break;
				case "=":
					leftValue = rightValue;
					break;
				case "&=":
					leftValue &= rightValue;
					break;
				case "<<=":
					leftValue <<= rightValue;
					break;
				case "|=":
					leftValue |= rightValue;
					break;
				case ">>=":
					leftValue >>= rightValue;
					break;
				case ">>>=":
					leftValue >>>= rightValue;
					break;
				case "^=":
					leftValue ^= rightValue;
					break;
				case "/=":
					leftValue /= rightValue;
					break;
				case "%=":
					leftValue %= rightValue;
					break;
				case "*=":
					leftValue *= rightValue;
					break;
				case "-=":
					leftValue -= rightValue;
					break;
				default:
					throw new FrontalError ( syntax.op.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.UNKNOWN_OPERATOR );
			}
			
			putValue ( left, leftValue );
			
			return right;
		}
		
		/**
		 * @private
		 */
		internal function parseConditionalExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseConditionalExpression" );
			
			var result : Object = parseBinaryExpression ( 4 );
			
			if ( peek ( ).token != "?" )
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseConditionalExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
				return result;
			}
			
			var newResult : Object = { objectType: "ConditionalExpression", cond: result };
			next ( );
			newResult.left = parseAssignmentExpression ( );
			expect ( [ ":" ] );
			newResult.right = parseAssignmentExpression ( );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseConditionalExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return newResult;
		}
		
		/**
		 * @private
		 */
		internal function evaluateConditionalExpression ( syntax : Object ) : Object
		{
			var result : Object = { objectType: "Value" };
			
			if ( getValue ( evaluateSyntax ( syntax.cond ) ) )
			{
				result.value = getValue ( evaluateSyntax ( syntax.left ) );
			}
			else
			{
				result.value = getValue ( evaluateSyntax ( syntax.right ) );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseBinaryExpression ( precedence : Number ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseBinaryExpression" );
			
			var result : Object = parseUnaryExpression ( );
			
			for ( var i : int = peek ( ).precedence; i >= precedence; i-- ) 
			{
				while ( peek ( ).precedence == i )
				{
					var op : Object = next ( );
					var y : Object = parseBinaryExpression ( i + 1 );
					result = { objectType : op.isCompareOperator ? "CompareOperation" : "BinaryOperation", op: op, x: result, y: y };
				}
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseBinaryExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateCompareOperation ( syntax : Object ) : Object
		{
			var xValue : * = getValue ( evaluateSyntax ( syntax.x ) );
			var yValue : * = getValue ( evaluateSyntax ( syntax.y ) );
			
			var result : Object = { objectType: "Value" };
			
			switch ( syntax.op.token )
			{
				case "==":
					result.value = xValue == yValue;
					break;
				case ">":
					result.value = xValue > yValue;
					break;
				case ">=":
					result.value = xValue >= yValue;
					break;
				case "!=":
					result.value = xValue != yValue;
					break;
				case "is":
					result.value = xValue is yValue;
					break;
				case "<":
					result.value = xValue < yValue;
					break;
				case "<=":
					result.value = xValue <= yValue;
					break;
				case "===":
					result.value = xValue === yValue;
					break;
				case "!==":
					result.value = xValue !== yValue;
					break;
				default:
					throw new FrontalError ( syntax.op.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.UNKNOWN_OPERATOR );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseUnaryExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseUnaryExpression" );
			
			var token : Object = peek ( );
			
			if ( token.isUnaryOp )
			{
				var result : Object = { objectType: "UnaryOperation" };
				result.op = next ( );
				result.x = parseUnaryExpression ( );
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseUnaryExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
				return result;
			}
			else if ( token.isCountOperator )
			{
				result = { objectType: "CountOperation" };
				result.prefix = true;
				result.op = next ( );
				result.x = parseUnaryExpression ( );
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseUnaryExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
				return result;
			}
			else
			{
				result = parsePostfixExpression ( );
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseUnaryExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
				return result;
			}
		}
		
		/**
		 * @private
		 */
		internal function evaluateUnaryOperation ( syntax : Object ) : Object
		{
			var rawXValue : Object = evaluateSyntax ( syntax.x );
			var xValue : * = getValue ( rawXValue );
			
			var result : Object = { objectType: "Value" };
			
			switch ( syntax.op.token )
			{
				case "!":
					result.value = ! xValue;
					break;
				case "typeof":
					result.value = typeof xValue;
					break;
				case "void":
					result.value = undefined;
					break;
				case "delete":
					result.value = delete rawXValue.baseObject [ rawXValue.propertyName ];
					break;
				case "~":
					result.value = ~xValue;
					break;
				case "+":
					result.value = +xValue;
					break;
				case "-":
					result.value = -xValue;
					break;
				default:
					throw new FrontalError ( syntax.op.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.UNKNOWN_OPERATOR );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parsePostfixExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parsePostfixExpression" );
			
			var result : Object = parseLeftHandSideExpression ( );
			
			// XXX We do not check that there is no line terminator before the
			// count operator.
			//
			
			if ( ! peek ( ).hasPrecedingLineTerminator && peek ( ).isCountOperator )
			{
				var newResult : Object = { objectType: "CountOperation" };
				newResult.prefix = false;
				newResult.op = next ( );
				newResult.x = result;
				result = newResult;
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parsePostfixExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateCountOperation ( syntax : Object ) : Object
		{
			var x : Object = evaluateSyntax ( syntax.x );
			var xValue : Number = Number ( getValue ( x ) );
			var result : Object = { objectType: "Value", value: xValue };
			
			switch ( syntax.op.token )
			{
				case "++":
					putValue ( x, xValue + 1 );
					if ( syntax.prefix )
					{
						result.value = xValue + 1;
					}
					break;
				case "--":
					putValue ( x, xValue - 1 );
					if ( syntax.prefix )
					{
						result.value = xValue - 1;
					}
					break;
				default:
					throw new FrontalError ( syntax.op.token + " at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.UNKNOWN_OPERATOR );
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseLeftHandSideExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseLeftHandSideExpression" );
			
			var result : Object;
			
			if ( peek ( ).token == "new" )
			{
				result = parseNewExpression ( );
			}
			else
			{
				result = parseMemberExpression ( );
			}
			
			var done : Boolean = false;
			
			while ( ! done )
			{
				switch ( peek ( ).token )
				{
					case "[":
						next ( );
						var newResult : Object = { objectType: "Property" };
						newResult.object = result;
						newResult.key = parseExpression ( );
						result = newResult;
						expect ( [ "]" ] );
						break;
						
					case "(":
						newResult = { objectType: "Call" };
						newResult.object = result;
						newResult.args = parseArguments ( );
						result = newResult;
						break;
						
					case ".":
						next ( );
						newResult = { objectType: "Property" };
						newResult.object = result;
						newResult.key = { objectType: "Value", value: parseIdentifier ( ).name };
						result = newResult;
						break;
						
					default:
						if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseLeftHandSideExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
						done = true;
						break;
				}
			}
			
			return result;
		}
			
		/**
		 * @private
		 */
		internal function evaluateProperty ( syntax : Object ) : Object
		{
			var objectResult : Object;
			var baseObject : Object;
			
			var key : Object = getValue ( evaluateSyntax ( syntax.key ) );
			
			try
			{
				objectResult = evaluateSyntax ( syntax.object );
				baseObject = getValue ( objectResult );
			}
			catch ( e : FrontalError )
			{
				if ( e.code != FrontalError.TYPE_ERROR ) throw e;
				
				// In AS3, we can't evaluate a class name element by element so
				// peek ahead to see if we can find a non-null object down the
				// road.
				//
				
				var propertyPath : String = String ( key );
				
				var scan : Object = syntax.object;
				
				while ( scan.objectType == "Property" )
				{
					if ( propertyPath.length > 0 ) propertyPath = "." + propertyPath;
					propertyPath = getValue ( evaluateSyntax ( scan.key ) ) + propertyPath;
					scan = scan.object;
				}
				
				if ( scan.objectType == "Identifier" ) 
				{
					if ( propertyPath.length > 0 ) propertyPath = "." + propertyPath;
					propertyPath = scan.name + propertyPath;
				}
				
				try
				{
					var value : * = getDefinition ( propertyPath );
				}
				catch ( e2 : Error )
				{
					throw e;
				}
				
				return { objectType: "Value", value: value };
			}
			
			if ( baseObject === null ) throw new FrontalError ( "requesting property \"" + key + "\" on a null object", FrontalError.TYPE_ERROR );
			
			return { objectType: "Reference", isPropertyReference: true, baseObject: baseObject, propertyName: key };
		}
		
		/**
		 * @private
		 */
		internal function evaluateCall ( syntax : Object ) : Object
		{
			var objectResult : Object = evaluateSyntax ( syntax.object );
			var argsResult : Object = evaluateSyntax ( syntax.args );
			
			var objectResultValue : * = getValue ( objectResult );
			var baseObject : Object = null;
			if ( objectResult.objectType == "Reference" && objectResult.isPropertyReference ) 
			{
				baseObject = objectResult.baseObject;
			
				if ( getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) && objectResultValue is Function && objectResultValue.hasOwnProperty ( "isFrontalFunction" ) )
				{
					// This is one of our functions. Let it know not to use the
					// implicit value of "this." See evaluateFunctionLiteral for
					// more details. (Also, that is where this flag gets reset
					// to false.)
					//
					objectResultValue.overrideImplicitThisValue = true;
				}
			}
	
			if ( objectResultValue == null && objectResult.hasOwnProperty ( "propertyName" ) ) throw new TypeError ( "Error #1010: The term \"" + objectResult.propertyName + "\" is undefined.");
			
			return { objectType: "Value", value: objectResultValue.apply ( baseObject, getValue ( argsResult ) ) }; 
		}
		
		/**
		 * @private
		 */
		internal function parseNewExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseNewExpression" );
			
			var newCount : Object = { count: 0 };
			
			while ( peek ( ).token == "new" )
			{
				next ( );
				newCount.count++;
			}
			
			if ( newCount.count <= 0 )
			{
				throw new FrontalError ( "Found \"" + peek ( ).token + "\". Expected \"new\" at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, peek ( ).codeIndex ), FrontalError.UNEXPECTED_TOKEN );
			}
			
			var result : Object = parseMemberWithNewPrefixesExpression ( newCount );
			
			if ( newCount.count > 0 )
			{
				var newResult : Object = { objectType: "CallNew" };
				newResult.object = result;
				result = newResult;
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseNewExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateCallNew ( syntax : Object ) : Object
		{
			var object : Object = getValue ( evaluateSyntax ( syntax.object ) );
			var args : Object = null; 
			
			if ( object == null ) throw new FrontalError ( "cannot resolve class for \"new\" call at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, syntax.op.codeIndex ), FrontalError.REFERENCE_ERROR );
			
			if ( syntax.args != undefined )
			{
				// AS2 does not allow a cast to Array so we keep args as an Object.
				//
				
				args = getValue ( evaluateSyntax ( syntax.args ) );
			}
			
			return { objectType: "Value", value: constructInstance ( object, args ) };
		}
		
		/**
		 * @private
		 */
		internal function parseMemberExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseMemberExpression" );
			
			var result : Object = parseMemberWithNewPrefixesExpression ( { count: 0 } );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseMemberExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parseMemberWithNewPrefixesExpression ( newCount : Object ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseMemberWithNewPrefixesExpression" );
			
			var result : Object;
			
			if ( peek ( ).token == "function" )
			{
				expect ( [ "function" ] );
				
				var name : Object;
				
				if ( peek ( ).token == "Identifier" )
				{
					name = parseIdentifier ( );
				}
				
				result = parseFunctionLiteral ( name );
			}
			else
			{
				result = parsePrimaryExpression ( );
			}
			
			var done : Boolean = false; 
			
			while ( ! done )
			{
				switch ( peek ( ).token )
				{
					case "[":
						next ( );
						var newResult : Object = { objectType: "Property" };
						newResult.object = result;
						newResult.key = parseExpression ( );
						result = newResult;
						expect ( [ "]" ] );
						break;
						
					case "(":
						if ( newCount.count == 0 )
						{
							if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseMemberWithNewPrefixesExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
							return result;
						}
						newResult = { objectType: "CallNew" };
						newResult.object = result;
						newResult.args = parseArguments ( );
						result = newResult;
						newCount.count--;
						break;
						
					case ".":
						next ( );
						newResult = { objectType: "Property" };
						newResult.object = result;
						newResult.key = { objectType: "Value", value: parseIdentifier ( ).name };
						result = newResult;
						break;
						
					default:
						if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseMemberWithNewPrefixesExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
						done = true;
						break;
				}
			}
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function parsePrimaryExpression ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parsePrimaryExpression" );
			
			var result : Object;
			
			switch ( peek ( ).token )
			{
				case "this":
					next ( );
					result = { objectType: "ThisValue" };
					break;
					
				case "null":
					next ( );
					result = { objectType: "Value", value: null };
					break;
					
				case "undefined":
					next ( );
					result = { objectType: "Value", value: undefined };
					break;
					
				case "true":
					next ( );
					result = { objectType: "Value", value: true };
					break;
					
				case "false":
					next ( );
					result = { objectType: "Value", value: false };
					break;
					
				case "Identifier":
					result = parseIdentifier ( );
					break;
					
				case "Number":
					result = { objectType: "Value", value: Number ( next ( ).value ) };
					break;
					
				case "String":
					result = { objectType: "Value", value: next ( ).value };
					break;
					
				case "[":
					result = parseArrayLiteral ( );
					break;
					
				case "{":
					result = parseObjectLiteral ( );
					break;
					
				case "(":
					next ( );
					result = parseExpression ( );
					expect ( [ ")" ] );
					break;
				
				default:
					throw new FrontalError ( peek ( ).token + "\". Expected \"new\ at " + tokenizer.getCodeSnippet ( currentCode, currentStatementCodeIndex, peek ( ).codeIndex ), FrontalError.UNEXPECTED_TOKEN );
					break;
			}
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parsePrimaryExpression returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateThisValue ( syntax : Object = null ) : Object
		{
			var currentExecutionContext : Object = executionContexts [ executionContexts.length - 1 ];
			return { objectType: "Value", value: currentExecutionContext.theThis };
		}
		
		/**
		 * @private
		 */
		internal function parseArrayLiteral ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseArrayLiteral" );
			
			var result : Object = { objectType: "ArrayLiteral", literals: [ ] };
			
			expect ( [ "[" ] );
			
			while ( peek ( ).token != "]" )
			{
				var elem : Object;
				
				if ( peek ( ).token == "," )
				{
					elem = { objectType: "Value", value: undefined };
				}
				else
				{
					elem = parseAssignmentExpression ( );
				}
				
				result.literals.push ( elem );
				
				if ( peek ( ).token != "]" )
				{
					expect ( [ "," ] );
				}
			}
			
			expect ( [ "]" ] );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseArrayLiteral returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
	
		/**
		 * @private
		 */
		internal function evaluateArrayLiteral ( syntax : Object ) : Object
		{
			var array : Array = [ ];
			
			for ( var i : int = 0; i < syntax.literals.length; i++ )
			{
				array [ i ] = getValue ( evaluateSyntax ( syntax.literals [ i ] ) );
			}
			
			return { objectType: "Value", value: array };
		}
		
		/**
		 * @private
		 */
		internal function parseObjectLiteral ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseObjectLiteral" );
			
			var result : Object = { objectType: "ObjectLiteral", properties: [ ] };
			
			expect ( [ "{" ] );
			
			while ( peek ( ).token != "}" )
			{
				var key : Object;
				
				switch ( peek ( ).token )
				{
					case "Identifier":
					case "String":
						key = { objectType: "Value", value: next ( ).value };
						break;
						
					case "Number":
						key = { objectType: "Value", value: Number ( next ( ).value ) };
						break;
						
					default:
						expect ( [ "}" ] );
						break;
				}
				
				expect ( [ ":" ] );
				
				var value : Object = parseAssignmentExpression ( );
				
				result.properties.push ( { objectType: "ObjectLiteralProperty", key: key, value: value } );
				
				if ( peek ( ).token != "}" )
				{
					expect ( [ "," ] );
				}
			}
			
			expect ( [ "}" ] );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseObjectLiteral returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateObjectLiteral ( syntax : Object ) : Object
		{
			var object : Object = { };
			
			for ( var i : int = 0; i < syntax.properties.length; i++ )
			{
				evaluateSyntax ( syntax.properties [ i ], object );
			}
			
			return { objectType: "Value", value: object };
		}
		
		/**
		 * @private
		 */
		internal function evaluateObjectLiteralProperty ( syntax : Object, object: Object ) : void
		{
			object [ getValue ( evaluateSyntax ( syntax.key ) ) ] = getValue ( evaluateSyntax ( syntax.value ) );
		}
		
		/**
		 * @private
		 */
		internal function parseArguments ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseArguments" );
			
			var result : Object = { objectType: "Arguments", arguments: [ ] };
			
			expect ( [ "(" ] );
			
			var done : Boolean = ( peek ( ).token == ")" );
			
			while ( ! done )
			{
				result.arguments.push ( parseAssignmentExpression ( ) );
				done = ( peek ( ).token == ")" );
				if ( ! done )
				{
					expect ( [ "," ] );
				}
			}
			
			expect ( [ ")" ] );
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseArguments returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateArguments ( syntax : Object ) : Object
		{
			var args : Array = [ ];
			
			for ( var i : int = 0; i < syntax.arguments.length; i++ )
			{
				args [ i ] = getValue ( evaluateSyntax ( syntax.arguments [ i ] ) );
			}
			
			return { objectType: "Value", value: args };
		}
		
		/**
		 * @private
		 */
		internal function parseIdentifier ( ) : Object
		{
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( 1 ) + "parseIdentifier" );
			
			var result : Object = { objectType: "Identifier", name: next ( ).value };
			
			if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_PARSETREE ) trace ( indentStackDepth ( -1 ) + "parseIdentifier returns " + Debugger.objectToString ( result, 0, 2, false ) );
			
			return result;
		}
		
		/**
		 * @private
		 */
		internal function evaluateIdentifier ( syntax : Object ) : Object
		{
			var result : Object = undefined;
			
			// First look in the var scopes.
			//
			
			var currentExecutionContext : Object = executionContexts [ executionContexts.length - 1 ];
			var currentScopeChain : Object = currentExecutionContext.scopeChain;
			
			for ( var i : uint = 0; i < currentScopeChain.length; i++ )
			{
				if ( currentScopeChain [ i ].hasOwnProperty ( syntax.name ) )
				{
					result = currentScopeChain [ i ];
					break;
				}
			}
			
			if ( syntax.skipThisScope !== true && result == null && currentExecutionContext.theThis != null ) 
			{ 
				// Now look in our "this" scope.
				//
				if ( currentExecutionContext.theThis.hasOwnProperty ( syntax.name ) )
				{
					result = currentExecutionContext.theThis;
				}
				else
				{
					var value : *;
					
					try
					{
						// XXX We're potentially calling a getter here which if
						// it calls the ActionScriptInterpreter in itself could
						// lead to an infinite loop.
						//
						value = currentExecutionContext.theThis [ syntax.name ];
						if ( value !== undefined ) result = currentExecutionContext.theThis;
					}
					catch ( e : Error )
					{
					}
				}
				
				// Else we treat it like a global variable.
			}
			
			return { objectType: "Reference", baseObject: result, propertyName: syntax.name };
		}
		
		/*
			Function: getValue
			
				Return the value of Reference, Value or raw object. See the
				ECMAScript specification for more details.
				
			Parameters:
			
				V	- A Reference, Value or raw object.
				
			Returns:
			
				The value of the object.
		*/
		/**
		 * @private
		 */
		internal function getValue ( V : * ) : *
		{ 
			if ( V == null ) return V;
			
			// XXX In one case where V was a Class (resulting from interpreting
			// "flash.utils.getDefinitionByName('Class');") then calling 
			// hasOwnProperty on it crashed the Flash player. What to do? These
			// tests returned true:
			//
			//   V is Object
			//   V [ "hasOwnProperty" ] is Function
			//
			// And we tried to put the call 'V.hasOwnProperty ( "objectType" )'
			// in a try statement but to no avail. So until we have a better 
			// solution, we check for the problematic Class value.
			//
			
			var isInterpObject : Boolean = false;
			if ( ! ( V is Class ) ) isInterpObject = V.hasOwnProperty ( "objectType" );
			
			if ( isInterpObject && V.objectType == "Reference" ) 
			{ 
				var baseObject : * = V.baseObject; 
				var value : *;
				
				if ( baseObject != null )
				{
					CONFIG::FLEX
					{
						// CS4 seems to behave differently than CS3. The CS3 
						// approach failed to get a working reference to 
						// Array.forEach for example. The changes allow it to. Well,
						// it actually gets a method closure which will only work if
						// applied to the "this" object in the closure. Flash bug I
						// believe.
						//
						value = baseObject [ V.propertyName ];
						
						// This check is to see if the baseObject has overridden the
						// [] operator. If it has, let's hope it's not returning a
						// function because we use property to decide if we should
						// look for a method on the class's prototype.
						//
						if ( ! ( value is Function ) )
						{
							var method : Function = null;
							
							try
							{
								var classRef : Class = getDefinition ( getQualifiedClassName ( baseObject ) ) as Class;					
								method = classRef.prototype [ V.propertyName ];
								if ( method != null ) value = method;
							}
							catch ( e : Error )
							{
							}
						}
					}
					
					CONFIG::CS3
					{
						/*
							For CS3:
						*/
							
						// Check if this a method. The issue is that 
						// baseObject [ V.propertyName ] returns null
						// for some methods. I think it's when the
						// method is static?
						//
						var method : Function = null;
						
						try
						{
							var classRef : Class = getDefinition ( getQualifiedClassName ( baseObject ) ) as Class;					
							method = classRef.prototype [ V.propertyName ];
						}
						catch ( e : Error )
						{
						}
						
						value = method != null ? method : baseObject [ V.propertyName ];
					}
				}
				else
				{
					try
					{
						value = getDefinition ( V.propertyName );
					}
					catch ( e : Error )
					{
					}
					
					// Look for it in the global Object.
					//
					if ( value === undefined && globalScope.hasOwnProperty ( V.propertyName ) ) value = globalScope [ V.propertyName ];
				}
			
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 0 ) + "get " + ( baseObject !== undefined ? "baseObject." : "" ) + V.propertyName + " = " + Debugger.objectToString ( value, 0, 2, false ) );
				
				return value;
			}
			else if ( isInterpObject && V.objectType == "Value" )
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 0 ) + "get " + Debugger.objectToString ( V.value, 0, 2, false ) );
				
				return V.value; 
			}
			else
			{
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 0 ) + "get " + Debugger.objectToString ( V, 0, 2, false ) );
				
				return V; 
			}
		}
		
		/*
			Function: putValue
			
				Set the value of a Reference object. See the ECMAScript 
				specification for more details.
				
			Parameters:
			
				V	- A Reference object.
				W	- A value for the object.
				
			Exceptions:
			
				Throws an error if V is not a Reference object.
		*/
		/**
		 * @private
		 */
		internal function putValue ( V : *, W : * ) : void
		{ 
			if ( V.objectType == "Reference" ) 
			{ 
				var baseObject : * = V.baseObject; 
				
				if ( baseObject != null )
				{
					baseObject [ V.propertyName ] = W;
				}
				else
				{
					globalScope [ V.propertyName ] = W;
				}
				
				if ( getObjectOption ( "actionscriptInterpreterDebug" ) & DEBUG_EVALUATE ) trace ( indentStackDepth ( 0 ) + "set " + ( baseObject !== undefined ? "baseObject." : "" ) + V.propertyName + " to " + Debugger.objectToString ( W, 0, 2, false ) );
			}
			else
			{
				throw new FrontalError ( "attempt to set non-reference to " + W, FrontalError.REFERENCE_ERROR );
			}
		}
		
		/*
			Function: constructInstance
			
				Construct an object from a class name and an array of arguments.
				So messy because Function.apply does not work with constructors.
				
			Parameters:
			
				classFunction	- The class of the object to construct.
				params			- An array of constructor arguments.
				
			Returns:
				
				The constructed instance.
				
			Notes:
			
				Only objects with fewer than 15 parameters may be constructed.
				
				We do not type the params argument because AS2 does not allow us to 
				cast from an Object to an Array and we don't want to burn cycles 
				rebuilding the array.
		*/
		/**
		 * @private
		 */
		internal function constructInstance ( classFunction : Object, params : * /* : Array */ ) : Object
		{
			var instance : Object = null;
			
			if ( getObjectOption ( "actionscriptInterpreterUseFunctionClosures" ) && classFunction.isFrontalFunction )
			{
				// It looks like one of our evaluated functions is being used as
				// a constructor. In this case, we don't want the closure to set
				// the functions "this" variable, but rather we want to use the
				// actual value of "this" as defined by the AS3 runtime. So we
				// remove the constructor's closure as well as those on all of
				// its member functions.
				//
				// After the object is constructed, we add the closures back on
				// the member functions so that they can be used as callbacks
				// for example.
				//
				// Here's an interesting related article:
				//
				// http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures
				//
				classFunction.isFrontalConstructor = true;
				classFunction.theThis = null;
				for ( var x : String in classFunction.prototype )
				{
					if ( classFunction.prototype [ x ] is Function && classFunction.prototype [ x ].theThis != null )
					{
						classFunction.prototype [ x ].theThis = null;
					}
				}
			}
	
			switch ( params.length )
			{
				case 0:
						instance = new classFunction ( );
						break; 
				case 1:
						instance = new classFunction ( params[0] );
						break;
				case 2:
						instance = new classFunction ( params[0], params[1] );
						break;
				case 3:
						instance = new classFunction ( params[0], params[1], params[2] );
						break;
				case 4:
						instance = new classFunction ( params[0], params[1], params[2], params[3] );
						break;
				case 5:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4] );
						break;
				case 6:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5] );
						break;
				case 7:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6] );
						break;
				case 8:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7] );
						break;
				case 9:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8] );
						break;
				case 10:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9] );
						break;
				case 11:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10] );
						break;
				case 12:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11] );
						break;
				case 13:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12] );
						break;
				case 14:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13] );
						break;
				case 15:
						instance = new classFunction ( params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9], params[10], params[11], params[12], params[13], params[14] );
						break;
				default:
						trace ( "WARNING: too many parameters" );
						break;
			}
			
			return instance;
		}
		
		/*
			The following functions add support for pretty getObjectOption ( "actionscriptInterpreterDebug" ) traces and errors.
		*/
		
		/**
		 * @private
		 */
		internal function indentStackDepth ( depthChange : Number ) : String
		{
			var indent : String = "";
			
			if ( depthChange <= 0 )
			{
				callStackDepth += depthChange;
				indent = callStackDepth + ": ";
			}
			
			for ( var i : int = 0; i < callStackDepth; i++ )
			{
				indent += " ";
			}
			
			if ( depthChange > 0 )
			{
				indent = callStackDepth + ": " + indent;
				callStackDepth += depthChange;
			}
			
			return indent;
		}
		
		/*
			Function: getDefinition
			
				If a document was provided for the evaluate call, use the ExternalAssetManager to get the definition
				in a more robust manner. Otherwise, use the standard method.
				
			Parameters: 
				
				name - a class name
				
			Returns: 
			
				a reference to the class object
				
		*/
		private function getDefinition ( name : String ) : *
		{
			var result : *;

			if ( documents.length == 0 || documents [ documents.length - 1 ] == null )
			{
				result = getDefinitionByName ( name );
			}
			else			
			{
				result = documents [ documents.length - 1 ].externalAssetManager.getDefinition ( name );
			
				if ( result == null ) throw new FrontalError ( "resolving \"" + name + "\"", FrontalError.REFERENCE_ERROR );
			}
			
			return result;
		}		
	}
}

